﻿using System.IO;
using PathFind.Algo;
using UnityEngine;
using UnityEngine.UI;

namespace PathFind {
    public class SquireEdit : MonoBehaviour {
        public SquireGrid grid;
        public InputDetector _inputDetector;
        public InputField inputWidth;
        public InputField inputHeight;
        public ClickAction clickAction = ClickAction.ChangeSetUnwalk;

        private IaStarAlgo[] _aStars;
        private int curAlgo = 0;
        private bool hasChangeGrid = false;

        private void Awake () {
            inputWidth.text = "20";
            inputHeight.text = "20";
            CreateMap(true);
        }

        public void CreateMap (bool refreshAlgo=false) {
            if (!short.TryParse(inputWidth.text, out short width)) {
                Debug.Log($"请先输入宽.");
                return;
            }

            if (!short.TryParse(inputHeight.text, out short height)) {
                Debug.Log($"请先输入高.");
                return;
            }
            grid.RefreshMap(width, height);
            var mainCamera = Camera.main;
            var centerPos = grid.GetCenterPos();
            System.Diagnostics.Debug.Assert(mainCamera != null, nameof(mainCamera) + " != null");
            mainCamera.transform.localPosition = new Vector3(centerPos.x, 10, centerPos.z);
            mainCamera.orthographicSize = centerPos.x + 3;
            if (refreshAlgo) {
                InitAlgo(width, height);
            }
        }

        public void InitAlgo (short width, short height) {
            var aStarRaw = gameObject.AddComponent<AStarRaw>();
            aStarRaw.ReInitData(width, height);
            var aStarJps = gameObject.AddComponent<AStarJps>();
            aStarJps.ReInitData(width, height);
            aStarJps.PreProcessGrid();
            var aStarBljps = gameObject.AddComponent<AStarBLJps2>();
            aStarBljps.ReInitData(width, height);
            aStarBljps.PreProcessGrid();
            _aStars = new IaStarAlgo[] {
                aStarRaw,
                aStarJps,
                aStarBljps,
            };
            foreach (var astar in _aStars) {
                astar.SetGrid(grid);
            }
        }

        public void ToggleToUnWalk (bool isTrigger) {
            if (isTrigger) {
                clickAction = ClickAction.ChangeSetUnwalk;
            }

            Debug.Log($"set to {clickAction} {isTrigger}");
        }

        public void ToggleToWalk (bool isTrigger) {
            if (isTrigger) {
                clickAction = ClickAction.ChangeSetWalk;
            }

            Debug.Log($"set to {clickAction} {isTrigger}");
        }

        public void ToggleToStartEnd (bool isTrigger) {
            if (isTrigger) {
                clickAction = ClickAction.ChangeStartEnd;
            }

            Debug.Log($"set to {clickAction} {isTrigger}");
        }

        public void StartPathFinding () {
            ClearPathColor(false);
            if (hasChangeGrid) {
                hasChangeGrid = false;
                ReProcessAlgo();
            }

            var algo = _aStars[curAlgo];
            var fromCell = _inputDetector.fromCell;
            var toCell = _inputDetector.toCell;
            var (startX, startY) = (fromCell.x, fromCell.y);
            var (endX, endY) = (toCell.x, toCell.y);
            StartCoroutine(algo.FindSolution(startX, startY, endX, endY));
        }

        public void ClearPathColor (bool clearStartEnd = false) {
            IaStarAlgo aStar = _aStars[0];
            var height = aStar.GetHeight();
            var width = aStar.GetWidth();
            if (clearStartEnd) {
                _inputDetector.fromCell = new Coordinate(-1, -1);
                _inputDetector.toCell = new Coordinate(-1, -1);
            }

            for (short i = 0; i < height; i++) {
                for (short j = 0; j < width; j++) {
                    var isPass = aStar.IsPassable(i, j);
                    if (_inputDetector.fromCell.IsEquals(i, j)) {
                        continue;
                    }

                    if (_inputDetector.toCell.IsEquals(i, j)) {
                        continue;
                    }

                    grid.ChangeColor(i, j, isPass ? Color.white : Color.black);
                }
            }
#if SHOW_GRID_TEXT
            grid.ClearAllLabel();
#endif
        }

        public void ReProcessAlgo () {
            foreach (var aStar in _aStars) {
                aStar.PreProcessGrid();
            }
        }

        public void ChangeAlgoSelect (int algoIndex) {
            curAlgo = algoIndex;
            Debug.Log($"change CurAlgo:{algoIndex}");
        }

        public void ChangeDistSelect (int distIndex) {
            Coordinate.CurDistFunc = Coordinate.DistFuncMap[distIndex];
            Debug.Log($"change Dist:{distIndex}");
        }

        public void StopAllPathFind () {
            StopAllCoroutines();
            foreach (var aStar in _aStars) {
                if(aStar is MonoBehaviour mono) {
                    mono.StopAllCoroutines();
                }
            }
        }

        public bool IsPassable (Coordinate clickCoord) {
            return _aStars[0].IsPassable(clickCoord);
        }

        public bool InBounds (Coordinate clickCoord) {
            return _aStars[0].InBounds(clickCoord);
        }

        public void SetPassable (Coordinate clickCoord, bool canWalk) {
            foreach (var aStar in _aStars) {
                aStar.SetPassable(clickCoord.x, clickCoord.y, canWalk);
            }

            grid.ChangeColor(clickCoord.x, clickCoord.y, canWalk ? Color.white : Color.black);
            hasChangeGrid = true;
        }

        public void SetStartPoint (Coordinate clickCoord) {
            grid.MultiplyColor(clickCoord.x, clickCoord.y, Color.red);
        }

        public void ResetColor (Coordinate clickCoord) {
            grid.ChangeColor(clickCoord.x, clickCoord.y, Color.white);
        }

        public void SetEndPoint (Coordinate clickCoord) {
            grid.ChangeColor(clickCoord.x, clickCoord.y, Color.blue);
        }

        public void WriteAlgoToFile (string fileName) {
            using (var bw = new BinaryWriter(File.Open(fileName, FileMode.Create))) {
                var aStar = _aStars[0];
                aStar.WriteDataToFile(bw);
            }
        }

        public void ReadAlgoFromFile (string fileName) {
            foreach (var aStar in _aStars) {
                using (var br = new BinaryReader(File.Open(fileName, FileMode.Open))) {
                    aStar.ReadDataFromFile(br);
                    aStar.PreProcessGrid();
                }
            }

            var algo = _aStars[0];
            RefreshMapWithAlgo(algo);
            for (short i = 0; i < algo.GetWidth(); i++) {
                for (short j = 0; j < algo.GetWidth(); j++) {
                    var canWalk = algo.IsPassable(i, j);
                    grid.ChangeColor(i, j, canWalk ? Color.white : Color.black);
                }
            }

        }

        private void RefreshMapWithAlgo (IaStarAlgo aStar) {
            inputWidth.text = aStar.GetWidth().ToString();
            inputHeight.text = aStar.GetHeight().ToString();
        }
    }
}

public enum ClickAction {
    ChangeSetUnwalk,
    ChangeSetWalk,
    ChangeStartEnd,
}